Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support reentrant calls to RakeCommand.perform #39137

Merged
merged 1 commit into from
May 4, 2020

Conversation

jonathanhefner
Copy link
Member

Rake stores the current top-level task and arguments in global state. Invoking another top-level task within the same process requires overwriting this state. Doing so indiscriminately can cause incorrect behavior, such as infinitely repeating the original task. In particular, this is a problem when running one task from another via rails_command "...", inline: true.

The solution is to save and restore the global state in each call to RakeCommand.perform using the Rake.with_application method.

Fixes #39128.

@rails-bot rails-bot bot added the railties label May 3, 2020
Rake.application.load_rakefile
Rake.application.top_level
Rake.with_application do |rake|
load "rails/tasks.rb"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unfortunate that this file has to be loaded each time, but I could not find a sanctioned way to copy loaded tasks from one instance to another. Neither Rake::Application nor Rake::TaskManager seem to have the necessary public accessors.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will reloading throw any constant redefined or similar warnings? Is it tough on performance?

Not sure if we know someone involved with Rake we could tap to help fit an API for this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was one constant redefined warning that I fixed: https://github.com/rails/rails/pull/39137/files#diff-94b3f444d3f09e55370aa7e94f440408R6

As for performance, I'm sure it is slower, but I didn't really notice the difference when I was repeatedly running tasks to try out the fix. And it should still beat shelling out and loading the entire Rails context in another process, which is what was happening before adding inline: true.

It would be cool if Rake supported this use case explicitly. Depending on how common this use case is, an option like Rake.with_application(preserve_tasks: true) would be pretty slick. Otherwise, something like Rake::Application#dup_without_top_level would be next best thing.

@jonathanhefner jonathanhefner force-pushed the reentrant-rake_command-perform branch from ecbfab0 to 6971f3b Compare May 3, 2020 23:56
Rake stores the current top-level task and arguments in global state.
Invoking another top-level task within the same process requires
overwriting this state.  Doing so indiscriminately can cause incorrect
behavior, such as infinitely repeating the original task.  In
particular, this is a problem when running one task from another via
`rails_command "...", inline: true`.

The solution is to save and restore the global state in each call to
`RakeCommand.perform` using the `Rake.with_application` method.

Fixes rails#39128.
@jonathanhefner jonathanhefner force-pushed the reentrant-rake_command-perform branch from 6971f3b to 68a3f67 Compare May 4, 2020 00:28
@kaspth kaspth merged commit 1d8e65a into rails:master May 4, 2020
@kaspth
Copy link
Contributor

kaspth commented May 4, 2020

Thanks!

@gerrywastaken
Copy link
Contributor

@jonathanhefner Do you have any thoughts on this issue I'm encountering where commands are run twice? Reverting your change appears to stop that particular issue: #40136

jonathanhefner added a commit to jonathanhefner/rails that referenced this pull request Aug 30, 2020
In rails#39137, a new `Rake::Application` instance was created per Rake
command invocation.  To ensure that the Rails tasks were defined for
each `Rake::Application`, `rails/tasks.rb` was loaded per instance.
However, `Rake::Application#load_rakefile` loads the application's
Rakefile, which should invoke `Rails.application.load_tasks`, which, in
turn, also loads the Rails tasks.  When a Rake task is defined more than
once, all definition blocks are executed when the task is run.  Hence,
Rails task blocks were being executed twice.

This commit removes the unnecessary load and avoids double execution.

Fixes rails#40136.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ActionText install command goes into recursive loop
3 participants